﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace XForms.BaseComponents
{
    public abstract class BaseUI : DrawableGameComponent
    {
        /// <summary>
        /// Gets or Sets the Name of this Screen
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// Gets or Sets the Title shown in the Title bar.  
        /// Leave blank for no Title bar to be displayed.
        /// </summary>
        public string Text { get; set; }

        /// <summary>
        /// Gets or Sets if the UI Components Title is displayed during rendering or not.
        /// </summary>
        public Boolean IsTextVisible { get; set; }

        /// <summary>
        /// Font used for displaying title text
        /// </summary>
        public SpriteFont Font { get; set; }

        /// <summary>
        /// Gets a reference to this UI components Textured Material used for rendering
        /// </summary>
        public Texture2D TextMaterial { get; protected set; }

        /// <summary>
        /// Gets a reference to this UI Components Color being used for rendering.
        /// </summary>
        public Color TextColor { get; protected set; }

        /// <summary>
        /// Gets or Sets if this Screen has a border
        /// </summary>
        public Boolean HasBorder { get; set; }

        /// <summary>
        /// Gets or Sets the Border Color if HasBorder=True
        /// </summary>
        public Color BorderColor { get; protected set; }

        /// <summary>
        /// Gets or Sets the Material used for the Border.
        /// </summary>
        public Texture2D BorderMaterial { get; protected set; }

        /// <summary>
        /// Gets or Sets if the border is drawn over or under the form
        /// </summary>
        public Boolean BorderOnTop { get; set; }

        /// <summary>
        /// Gets or Sets if this Screen is visible or not.
        /// </summary>
        public Boolean Visible { get; protected set; }

        /// <summary>
        /// Gets or Sets if this UI Component can be closed or not
        /// </summary>
        public Boolean CanClose { get; set; }

        /// <summary>
        /// Gets or Sets the background color if no Material is used.
        /// </summary>
        public Color BackgroundColor { get; protected set; }

        /// <summary>
        /// Gets or Sets the Texture used for the Screens background image.
        /// </summary>
        public Texture2D BackgroundMaterial { get; protected set; }

        /// <summary>
        /// Gets or Sets how transparent this screen will be.  This does not affect the Controls.
        /// </summary>
        public float Transparency { get; set; }

        /// <summary>
        /// Gets or Sets the Radians used for rotation.
        /// </summary>
        public float Rotation { get; set; } //TODO: Convert to Int for degrees.  Draw() methods can use a Convert() method to get to Radians.

        /// <summary>
        /// Gets or Sets if this Screen is hidden but still running.
        /// </summary>
        public Boolean IsHidden { get; set; }

        /// <summary>
        /// Gets or reference to the Game Type that the UI is referencing.
        /// </summary>
        public Game Game { get; protected set; }

        /// <summary>
        /// Gets or Sets the Location of this UI component.
        /// </summary>
        public Vector2 Location { get; set; }

        /// <summary>
        /// Gets or Sets the Size of this UI Component.
        /// </summary>
        public Size Size { get; set; }

        public Vector2 Center
        {
            get
            {
                int x, y;
                x = (int)this.Location.X + this.Size.Width / 2;
                y = (int)this.Location.Y + this.Size.Height / 2;
                return new Vector2(x, y);
            }
        }

        /// <summary>
        /// The order in which to draw this UI Component when overlapped with multiple components.
        /// </summary>
        public int DrawOrder { get; set; }

        /// <summary>
        /// Gets or Sets if this UI Component can be moved or not.
        /// </summary>
        public Boolean IsStatic { get; set; }

        public Boolean IsMouseOver { get; protected set; }

        public Boolean IsMouseDown { get; protected set; }

        public Boolean IsMoving { get; protected set; }

        /// <summary>
        /// Gets a Reference to the Screens Viewport Size.
        /// </summary>
        public Rectangle Interface
        {
            get
            {
                return new Rectangle((int)Location.X, (int)Location.Y, Size.Width, Size.Height);
            }
        }

        public Vector2 ControlOrigin { get; set; }

        public Vector2 MouseOrigin { get; set; }

        /// <summary>
        /// Gets a reference to the Screens Sprite Batch for rendering to the back buffer.
        /// </summary>
        protected SpriteBatch SpriteBatch { get; set; }

        public BaseUI(Game game, String name) : base(game)
        {
            //Assign a reference the currently running game.
            this.Game = game;

            //Save the UI components name.
            if (String.IsNullOrEmpty(name))
                this.Name = "New " + this.GetType().Name;
            else
                this.Name = name;
            this.Text = this.Name;

            //This will always be invisible when first created.
            //Allows developers to prepare it prior to showing.
            //Developers call BaseUI.Show() to display it when ready.
            this.Visible = false;
            this.IsHidden = false;

            //Regardless if this is true, it will not be displayed
            //when BaseUI.Visible = false;
            this.IsTextVisible = true;

            //Setup a default Location & Size
            this.Location = new Vector2(0, 0);
            this.Size = new Size(100, 50);

            //Default UI Components have no borders.
            this.HasBorder = false;
            this.BorderOnTop = true;

            //Setup default colors.
            this.BorderColor = Color.DarkGray;
            this.TextColor = Color.White;
            this.BackgroundColor = Color.Gray;

            //Default Alpha value
            this.Transparency = 0;

            //Set the default font.
            this.SetFont("DefaultFont");
        }

        /// <summary>
        /// This method is automatically called during instancing.
        /// </summary>
        public override void Initialize()
        {
            //Try to instance objects that need access to the graphics device.
            //If they fail, then it is safe to assume that the graphics device is not
            //instanced yet.
            try
            {
                this.SpriteBatch = new SpriteBatch(this.Game.GraphicsDevice);

                //Create the border textures that will be rendered
                this.BorderMaterial = new Texture2D(this.Game.GraphicsDevice, 1, 1);
                this.BorderMaterial.SetData(new Color[] { BorderColor });

                //Create the background textures that will be rendered
                this.BackgroundMaterial = new Texture2D(this.Game.GraphicsDevice, 1, 1);
                this.BackgroundMaterial.SetData(new Color[] { BackgroundColor });

                this.TextMaterial = new Texture2D(this.Game.GraphicsDevice, 1, 1);
                this.TextMaterial.SetData(new Color[] { TextColor });
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message.ToString() + "\nHave you instanced the graphics device yet?\n\n");
            }

            base.Initialize();
        }

        /// <summary>
        /// Sets the background color.
        /// </summary>
        /// <param name="color"></param>
        public virtual void SetBackgroundColor(Color color)
        {
            this.BackgroundColor = color;
            this.BackgroundMaterial = new Texture2D(this.Game.GraphicsDevice, 1, 1);
            this.BackgroundMaterial.SetData(new Color[] { color });
        }

        /// <summary>
        /// Sets the background to the specified texture material.
        /// </summary>
        /// <param name="material"></param>
        public virtual void SetBackgroundMaterial(Texture2D material)
        {
            if (material == null) return;

            this.BackgroundMaterial = new Texture2D(this.Game.GraphicsDevice, material.Width, material.Height);
            this.BackgroundMaterial = material;
        }

        /// <summary>
        /// Sets the background to the specified texture material.
        /// </summary>
        /// <param name="material"></param>
        public virtual void SetBackgroundMaterial(string materialName)
        {
            if (String.IsNullOrEmpty(materialName)) 
                return;

            Texture2D material = this.Game.Content.Load<Texture2D>(materialName);
            this.BackgroundMaterial = new Texture2D(this.Game.GraphicsDevice, material.Width, material.Height);
            this.BackgroundMaterial = material;
        }

        /// <summary>
        /// Sets the UI Components Text color
        /// </summary>
        /// <param name="color"></param>
        public virtual void SetTextColor(Color color)
        {
            this.TextColor = color;
            this.TextMaterial = new Texture2D(this.Game.GraphicsDevice, 1, 1);
            this.TextMaterial.SetData(new Color[] { color });
        }

        /// <summary>
        /// Sets the UI Components Text to the specified texture material.
        /// </summary>
        /// <param name="material"></param>
        public virtual void SetTextMaterial(Texture2D material)
        {
            if (material == null) return;

            this.TextMaterial = new Texture2D(this.Game.GraphicsDevice, material.Width, material.Height);
            this.TextMaterial = material;
        }

        /// <summary>
        /// Sets the background to the specified texture material.
        /// </summary>
        /// <param name="material"></param>
        public virtual void SetTextMaterial(string materialName)
        {
            if (String.IsNullOrEmpty(materialName))
                return;

            Texture2D material = this.Game.Content.Load<Texture2D>(materialName);
            this.TextMaterial = new Texture2D(this.Game.GraphicsDevice, material.Width, material.Height);
            this.TextMaterial = material;
        }

        /// <summary>
        /// Sets the UI Components border color.
        /// </summary>
        /// <param name="color"></param>
        public virtual void SetBorderColor(Color color)
        {
            this.BorderColor = color;
            this.BorderMaterial = new Texture2D(this.Game.GraphicsDevice, 1, 1);
            this.BorderMaterial.SetData(new Color[] { color });
        }

        /// <summary>
        /// Sets the UI Components border to the specified texture material.
        /// </summary>
        /// <param name="material"></param>
        public virtual void SetBorderMaterial(Texture2D material)
        {
            if (material == null) return;

            this.BorderMaterial = new Texture2D(this.Game.GraphicsDevice, material.Width, material.Height);
            this.BorderMaterial = material;
        }

        /// <summary>
        /// Sets the background to the specified texture material.
        /// </summary>
        /// <param name="material"></param>
        public virtual void SetBorderMaterial(string materialName)
        {
            if (String.IsNullOrEmpty(materialName))
                return;

            Texture2D material = this.Game.Content.Load<Texture2D>(materialName);
            this.BorderMaterial = new Texture2D(this.Game.GraphicsDevice, material.Width, material.Height);
            this.BorderMaterial = material;
        }

        /// <summary>
        /// Sets the UI Components Font to the specified Font.
        /// The specified font content must already processed via the content pipeline.
        /// </summary>
        /// <param name="fontName"></param>
        /// <returns></returns>
        public virtual Boolean SetFont(string fontName)
        {
            //Check if the font name is empty.
            //If so, return false.
            if (String.IsNullOrEmpty(fontName))
                return false;

            //Attempt to load the content from the content pipeline.
            //If it fails, return false.
            try
            {
                this.Font = this.Game.Content.Load<SpriteFont>(fontName);
            }
            catch (Exception ex)
            {
                return false;
            }

            //No failure, so we must have loaded without issue.
            return true;
        }

        /// <summary>
        /// Sets the UI Components Font to the specified Font.
        /// The specified font content must already processed via the content pipeline.
        /// </summary>
        /// <param name="font"></param>
        /// <returns></returns>
        public virtual Boolean SetFont(SpriteFont font)
        {
            //Check if the font is null.
            if (font == null)
                return false;
            //If not, then assign to this components Font Property
            else
                this.Font = font;

            //If we made it this far, then the Font was assigned without issue.
            return true;
        }

        protected virtual void UpdateMousePosition(GameTime gameTime)
        {
            MouseState mouse = Mouse.GetState();

            if (!this.IsMoving)
            {
                Boolean mouseX, mouseY;

                if ((mouse.X >= this.Location.X) && (mouse.X <= this.Location.X + this.Size.Width))
                    mouseX = true;
                else
                    mouseX = false;
                if ((mouse.Y >= this.Location.Y) && (mouse.Y <= this.Location.Y + this.Size.Height))
                    mouseY = true;
                else
                    mouseY = false;

                if (mouseX && mouseY)
                    this.IsMouseOver = true;
                else
                    this.IsMouseOver = false;
            }
        }

        public override void Draw(GameTime gameTime) { base.Draw(gameTime); } //Stub

        public override void Update(GameTime gameTime) { base.Update(gameTime); } //Stub

        public abstract void Show();

        public abstract void Close();

        public abstract void Hide();

        public abstract void OnShow(BaseUI sender);

        public abstract void OnClose(BaseUI sender, out Boolean cancel);

        public abstract void OnHide(BaseUI sender);

        public abstract void OnUpdate(BaseUI sender, GameTime gameTime);

        public abstract void OnDraw(BaseUI sender, GameTime gameTime);

        public abstract void OnClick(BaseUI sender);

        public abstract void OnMove(BaseUI sender);

        public abstract void OnMouseOver(BaseUI sender);

        public event EventHandler<EventArgs> DrawOrderChanged;

        public event EventHandler<EventArgs> VisibleChanged;

        public bool Enabled { get; set; }

        public event EventHandler<EventArgs> EnabledChanged;

        public int UpdateOrder { get; set; }

        public event EventHandler<EventArgs> UpdateOrderChanged;
    }
}
